package com.yalantis.phoenix.refresh_view

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.drawable.Animatable
import android.view.animation.Animation
import android.view.animation.Interpolator
import android.view.animation.LinearInterpolator
import android.view.animation.Transformation
import com.hupu.jrs.R
import com.yalantis.phoenix.PullToRefreshView
import com.yalantis.phoenix.util.Logger
import com.yalantis.phoenix.util.Utils

/**
 * Created by Oleksii Shliama on 22/12/2014.
 * https://dribbble.com/shots/1650317-Pull-to-Refresh-Rentals
 */
class SunRefreshView(context: Context, private val mParent: PullToRefreshView) : BaseRefreshView(context, mParent), Animatable {
    private val mMatrix: Matrix
    private var mAnimation: Animation? = null

    private var mTop: Int = 0
    private var mScreenWidth: Int = 0

    private var mSkyHeight: Int = 0
    private var mSkyTopOffset: Float = 0.toFloat()
    private var mSkyMoveOffset: Float = 0.toFloat()

    private var mTownHeight: Int = 0
    private var mTownInitialTopOffset: Float = 0.toFloat()
    private var mTownFinalTopOffset: Float = 0.toFloat()
    private var mTownMoveOffset: Float = 0.toFloat()

    private val mSunSize = 100
    private var mSunLeftOffset: Float = 0.toFloat()
    private var mSunTopOffset: Float = 0.toFloat()

    private var mPercent = 0.0f
    private var mRotate = 0.0f

    private var mSky: Bitmap? = null
    private var mSun: Bitmap? = null
    private var mTown: Bitmap? = null

    private var isRefreshing = false

    var isCreateBitmap = false
        private set

    init {
        mMatrix = Matrix()

        setupAnimations()
        mParent.post { initiateDimens(this.mParent.width) }
    }

    fun initiateDimens(viewWidth: Int) {
        Logger.d("viewWidth:" + viewWidth)
        if (viewWidth <= 0 || viewWidth == mScreenWidth) return

        mScreenWidth = viewWidth
        mSkyHeight = (SKY_RATIO * mScreenWidth).toInt()
        mSkyTopOffset = mSkyHeight * 0.38f
        mSkyMoveOffset = Utils.convertDpToPixel(this!!.context!!, 15).toFloat()

        mTownHeight = (TOWN_RATIO * mScreenWidth).toInt()
        mTownInitialTopOffset = mParent.totalDragDistance - mTownHeight * TOWN_INITIAL_SCALE
        mTownFinalTopOffset = mParent.totalDragDistance - mTownHeight * TOWN_FINAL_SCALE
        mTownMoveOffset = Utils.convertDpToPixel(this!!.context!!, 10).toFloat()

        mSunLeftOffset = 0.3f * mScreenWidth.toFloat()
        mSunTopOffset = mParent.totalDragDistance * 0.1f

        mTop = -mParent.totalDragDistance

        createBitmaps()
    }

    private fun createBitmaps() {
        Logger.d("createBitmaps")
        val options = BitmapFactory.Options()
        options.inPreferredConfig = Bitmap.Config.RGB_565

        mSky = BitmapFactory.decodeResource(context!!.resources, R.drawable.sky, options)
        mSky = Bitmap.createScaledBitmap(mSky!!, mScreenWidth, mSkyHeight, true)
        mTown = BitmapFactory.decodeResource(context!!.resources, R.drawable.buildings, options)
        mTown = Bitmap.createScaledBitmap(mTown!!, mScreenWidth, (mScreenWidth * TOWN_RATIO).toInt(), true)

        mSun = BitmapFactory.decodeResource(context!!.resources, R.drawable.sun, options)
        mSun = Bitmap.createScaledBitmap(mSun!!, mSunSize, mSunSize, true)

        Logger.d("mTown:" + mTown!!.toString())
        Logger.d("mSky:" + mSky!!.toString())
        Logger.d("mSun:" + mSun!!.toString())
        isCreateBitmap = true
    }

    override fun setPercent(percent: Float, invalidate: Boolean) {
        setPercent(percent)
        if (invalidate) setRotate(percent)
    }

    override fun offsetTopAndBottom(offset: Int) {
        mTop += offset
        invalidateSelf()
    }

    override fun draw(canvas: Canvas) {
        if (mScreenWidth <= 0) return

        val saveCount = canvas.save()

        canvas.translate(0f, mTop.toFloat())
        canvas.clipRect(0, -mTop, mScreenWidth, mParent.totalDragDistance)

        drawSky(canvas)
        drawSun(canvas)
        drawTown(canvas)

        canvas.restoreToCount(saveCount)
    }

    private fun drawSky(canvas: Canvas) {
        val matrix = mMatrix
        matrix.reset()

        val dragPercent = Math.min(1f, Math.abs(mPercent))

        val skyScale: Float
        val scalePercentDelta = dragPercent - SCALE_START_PERCENT
        if (scalePercentDelta > 0) {
            /** Change skyScale between [.SKY_INITIAL_SCALE] and 1.0f depending on [.mPercent]  */
            val scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT)
            skyScale = SKY_INITIAL_SCALE - (SKY_INITIAL_SCALE - 1.0f) * scalePercent
        } else {
            skyScale = SKY_INITIAL_SCALE
        }

        val offsetX = -(mScreenWidth * skyScale - mScreenWidth) / 2.0f
        val offsetY = (((1.0f - dragPercent) * mParent.totalDragDistance - mSkyTopOffset
                // Offset canvas moving
                - mSkyHeight * (skyScale - 1.0f) / 2) // Offset sky scaling
                + mSkyMoveOffset * dragPercent) // Give it a little move top -> bottom

        matrix.postScale(skyScale, skyScale)
        matrix.postTranslate(offsetX, offsetY)
        canvas.drawBitmap(mSky!!, matrix, null)
    }

    private fun drawTown(canvas: Canvas) {
        val matrix = mMatrix
        matrix.reset()

        val dragPercent = Math.min(1f, Math.abs(mPercent))

        val townScale: Float
        val townTopOffset: Float
        val townMoveOffset: Float
        val scalePercentDelta = dragPercent - SCALE_START_PERCENT
        if (scalePercentDelta > 0) {
            /**
             * Change townScale between [.TOWN_INITIAL_SCALE] and [.TOWN_FINAL_SCALE] depending on [.mPercent]
             * Change townTopOffset between [.mTownInitialTopOffset] and [.mTownFinalTopOffset] depending on [.mPercent]
             */
            val scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT)
            townScale = TOWN_INITIAL_SCALE + (TOWN_FINAL_SCALE - TOWN_INITIAL_SCALE) * scalePercent
            townTopOffset = mTownInitialTopOffset - (mTownFinalTopOffset - mTownInitialTopOffset) * scalePercent
            townMoveOffset = mTownMoveOffset * (1.0f - scalePercent)
        } else {
            val scalePercent = dragPercent / SCALE_START_PERCENT
            townScale = TOWN_INITIAL_SCALE
            townTopOffset = mTownInitialTopOffset
            townMoveOffset = mTownMoveOffset * scalePercent
        }

        val offsetX = -(mScreenWidth * townScale - mScreenWidth) / 2.0f
        val offsetY = (((1.0f - dragPercent) * mParent.totalDragDistance // Offset canvas moving
                + townTopOffset) - mTownHeight * (townScale - 1.0f) / 2 // Offset town scaling
                + townMoveOffset) // Give it a little move

        matrix.postScale(townScale, townScale)
        matrix.postTranslate(offsetX, offsetY)

        canvas.drawBitmap(mTown!!, matrix, null)
    }

    private fun drawSun(canvas: Canvas) {
        val matrix = mMatrix
        matrix.reset()

        var dragPercent = mPercent
        if (dragPercent > 1.0f) { // Slow down if pulling over set height
            dragPercent = (dragPercent + 9.0f) / 10
        }

        val sunRadius = mSunSize.toFloat() / 2.0f
        var sunRotateGrowth = SUN_INITIAL_ROTATE_GROWTH

        var offsetX = mSunLeftOffset
        var offsetY = (mSunTopOffset + mParent.totalDragDistance / 2 * (1.0f - dragPercent)
                // Move the sun up
                - mTop) // Depending on Canvas position

        val scalePercentDelta = dragPercent - SCALE_START_PERCENT
        if (scalePercentDelta > 0) {
            val scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT)
            val sunScale = 1.0f - (1.0f - SUN_FINAL_SCALE) * scalePercent
            sunRotateGrowth += (SUN_FINAL_ROTATE_GROWTH - SUN_INITIAL_ROTATE_GROWTH) * scalePercent

            matrix.preTranslate(offsetX + (sunRadius - sunRadius * sunScale),
                    offsetY * (2.0f - sunScale))
            matrix.preScale(sunScale, sunScale)

            offsetX += sunRadius
            offsetY = offsetY * (2.0f - sunScale) + sunRadius * sunScale
        } else {
            matrix.postTranslate(offsetX, offsetY)

            offsetX += sunRadius
            offsetY += sunRadius
        }

        matrix.postRotate((if (isRefreshing) -360 else 360).toFloat() * mRotate * if (isRefreshing) 1L else sunRotateGrowth,
                offsetX, offsetY)

        canvas.drawBitmap(mSun!!, matrix, null)
    }

    fun setPercent(percent: Float) {
        mPercent = percent
    }

    fun setRotate(rotate: Float) {
        mRotate = rotate
        invalidateSelf()
    }

    fun resetOriginals() {
        setPercent(0f)
        setRotate(0f)
    }

    override fun onBoundsChange(bounds: Rect) {
        super.onBoundsChange(bounds)
    }

    override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
        super.setBounds(left, top, right, mSkyHeight + top)
    }

    override fun isRunning(): Boolean {
        return false
    }

    override fun start() {
        mAnimation!!.reset()
        isRefreshing = true
        mParent.startAnimation(mAnimation)
    }

    override fun stop() {
        mParent.clearAnimation()
        isRefreshing = false
        resetOriginals()
    }

    private fun setupAnimations() {
        mAnimation = object : Animation() {
            public override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
                setRotate(interpolatedTime)
            }
        }
        mAnimation!!.repeatCount = Animation.INFINITE
        mAnimation!!.repeatMode = Animation.RESTART
        mAnimation!!.interpolator = LINEAR_INTERPOLATOR
        mAnimation!!.duration = ANIMATION_DURATION.toLong()
    }

    companion object {

        private val SCALE_START_PERCENT = 0.5f
        private val ANIMATION_DURATION = 1000

        private val SKY_RATIO = 0.65f
        private val SKY_INITIAL_SCALE = 1.05f

        private val TOWN_RATIO = 0.22f
        private val TOWN_INITIAL_SCALE = 1.20f
        private val TOWN_FINAL_SCALE = 1.30f

        private val SUN_FINAL_SCALE = 0.75f
        private val SUN_INITIAL_ROTATE_GROWTH = 1.2f
        private val SUN_FINAL_ROTATE_GROWTH = 1.5f

        private val LINEAR_INTERPOLATOR = LinearInterpolator()
    }
}
